package ModelLayer;

import java.util.Calendar;
import java.util.Date;

public class BookingYear2 {
	private int year;
	private int[][] availableRooms; // first index is the month of the year,
									// second index is day number of the month,
									// value is available rooms

	public BookingYear2(RoomType roomType, int year) {
		this.year = year;
		availableRooms = new int[12][31]; // [12] represent 12 months, [31]
											// represent 31 slots for a month,
											// value is available room number of
											// the day
		int norOfRooms = 0;
		// get availabler rooms number of the roomtype
		// for (Room r : RoomRecords.getInstance().getRooms()) {
		// if (r.getRoomType() == roomType) {
		// norOfRooms++;
		// }
		// }
		Calendar calendar = Calendar.getInstance();
		calendar.clear();
		calendar.set(Calendar.YEAR, year); // set year
		for (int i = 0; i < 12; i++) {
			calendar.set(Calendar.MONTH, i); // set month
			for (int j = 0; j < 31; j++) {
				// when j value does not exceed maximum days of that month,
				// available rooms number are assigned
				if (j < calendar.getMaximum(Calendar.DAY_OF_MONTH)) {
					availableRooms[i][j] = norOfRooms;
				} else
					availableRooms[i][j] = 0;
			}
		}
	}

	public void roomBooking(Date startDate, Date endDate)
			throws BookingException {
		int startMonth = getMonth(startDate);
		int endMonth = getMonth(endDate);
		int monthDif = endMonth - startMonth;
		if (monthDif == 0) {
			bookToEnd(startDate, endDate);
		} else if (monthDif == 1) {
			bookToEnd(startDate, getEndDateOfMonth(startMonth));
			bookToEnd(getStartDateOfMonth(endMonth), endDate);
		} else {
			// book a room for the first booking month from start date
			bookToEnd(startDate, getEndDateOfMonth(startMonth));
			for (int i = 1; i < monthDif; i++) {
				// book a room for the months between start date month and end
				// date month
				bookMonth(startMonth + i);
			}
			// book a room for the last month from start of the month to end
			// date
			bookToEnd(getStartDateOfMonth(endMonth), endDate);
		}
	}

	public void cancelRoomBooking(Date startDate, Date endDate) {
		int startMonth = getMonth(startDate);
		int endMonth = getMonth(endDate);
		int monthDif = endMonth - startMonth;
		if (monthDif == 0) {
			cancelToEnd(startDate, endDate);
		} else if (monthDif == 1) {
			cancelToEnd(startDate, getEndDateOfMonth(startMonth));
			cancelToEnd(getStartDateOfMonth(endMonth), endDate);
		} else {
			// cancel a room for the first booking month from start date
			cancelToEnd(startDate, getEndDateOfMonth(startMonth));
			for (int i = 1; i < monthDif; i++) {
				// cancel a room for the months between start date month and end
				// date month
				cancelMonth(startMonth + i);
			}
			// cancel a room for the last month from start of the month to end
			// date
			cancelToEnd(getStartDateOfMonth(endMonth), endDate);
		}
	}

	public boolean isAvailableRoom(Date startDate, Date endDate) {

		int startMonth = getMonth(startDate);
		int endMonth = getMonth(endDate);
		int monthDif = endMonth - startMonth;
		if (monthDif == 0) {
			if (!checkToEnd(startDate, endDate))
				return false;
		} else if (monthDif == 1) {
			if (!checkToEnd(startDate, getEndDateOfMonth(startMonth))
					|| !checkToEnd(getStartDateOfMonth(endMonth), endDate)) {
				return false;
			}
		} else {
			if (!checkToEnd(startDate, getEndDateOfMonth(startMonth)))
				return false; // check the first booking month from start date
			for (int i = 1; i < monthDif; i++) {
				if (!checkMonth(startMonth + i))
					return false;// check the months between start date month
									// and end date month
			}
			if (!checkToEnd(getStartDateOfMonth(endMonth), endDate))
				return false;
			// check the last month from start of the month to end date
		}

		return true;
	}

	// check whether room type is available from start date to the end date,
	// when dates are in the same month
	public boolean checkToEnd(Date startDate, Date endDate) {

		int month = getMonth(startDate);
		int start;
		int end = getDate(endDate);
		for (start = getDate(startDate) - 1; start < (end - 1); start++) {
			if (availableRooms[month][start] - 1 < 0) {
				return false;
			}
		}
		return true;
	}

	// check whether room type is available for a whole monoth
	public boolean checkMonth(int month) {

		Calendar calendar = Calendar.getInstance();
		calendar.clear();
		calendar.set(Calendar.YEAR, year);
		calendar.set(Calendar.MONTH, month);
		int maxDays = calendar.getMaximum(Calendar.DAY_OF_MONTH);
		for (int start = 0; start < maxDays; start++) {
			if (availableRooms[month][start] - 1 < 0) {
				return false;
			}
		}
		return true;
	}

	// book a room from start date to end date, when dates are in the same month
	public void bookToEnd(Date startDate, Date endDate) throws BookingException {
		int month = getMonth(startDate);
		int start;
		int end = getDate(endDate);
		// book to the date before endDate, (end - start - 1)are the number of
		// nights the guest stays
		for (start = getDate(startDate) - 1; start < (end - 1); start++) {
			if (availableRooms[month][start] - 1 < 0) {
				throw new BookingException(
						"Room booking not successful, not all the dates are available for booking");
			}
			availableRooms[month][start] = availableRooms[month][start] - 1;
		}

	}

	// book a room for a whole month
	public void bookMonth(int month) throws BookingException {
		Calendar calendar = Calendar.getInstance();
		calendar.clear();
		calendar.set(Calendar.YEAR, year);
		calendar.set(Calendar.MONTH, month);
		int maxDays = calendar.getMaximum(Calendar.DAY_OF_MONTH);
		for (int start = 0; start < maxDays; start++) {
			if (availableRooms[month][start] - 1 < 0) {
				throw new BookingException(
						"Room booking not successful, not all the dates are available for booking");
			}
			availableRooms[month][start] = availableRooms[month][start] - 1;
		}
	}

	// book a room from start date to end date, when dates are in the same month
	public void cancelToEnd(Date startDate, Date endDate) {
		int month = getMonth(startDate);
		int start;
		int end = getDate(endDate);
		// cancel to the date before endDate, (end - start - 1)are the number of
		// nights the guest stays
		for (start = getDate(startDate) - 1; start < (end - 1); start++) {
			availableRooms[month][start] = availableRooms[month][start] + 1;
		}

	}

	// book a room for a whole month
	public void cancelMonth(int month) {
		Calendar calendar = Calendar.getInstance();
		calendar.clear();
		calendar.set(Calendar.YEAR, year);
		calendar.set(Calendar.MONTH, month);
		int maxDays = calendar.getMaximum(Calendar.DAY_OF_MONTH);
		for (int start = 0; start < maxDays; start++) {
			availableRooms[month][start] = availableRooms[month][start] + 1;
		}
	}

	public int getMonth(Date date) {
		Calendar c = Calendar.getInstance();
		c.setTime(date);
		int month = c.get(Calendar.MONTH);
		return month;
	}

	public int getDate(Date date) {
		Calendar c = Calendar.getInstance();
		c.setTime(date);
		int dateNr = c.get(Calendar.DATE);
		return dateNr;
	}

	// return start date of a month
	public Date getStartDateOfMonth(int month) {
		Calendar calendar = Calendar.getInstance();
		calendar.clear();
		calendar.set(Calendar.YEAR, year);
		calendar.set(Calendar.MONTH, month);
		calendar.set(Calendar.DAY_OF_MONTH, 1);
		Date startDate = calendar.getTime();
		return startDate;
	}

	// return end date of a month
	public Date getEndDateOfMonth(int month) {
		Calendar calendar = Calendar.getInstance();
		calendar.clear();
		calendar.set(Calendar.YEAR, year);
		calendar.set(Calendar.MONTH, month);
		calendar.set(Calendar.DAY_OF_MONTH, 31);
		Date endDate = calendar.getTime();
		return endDate;
	}

	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}

	public int[][] getAvailableRooms() {
		return availableRooms;
	}

	public void setAvailableRooms(int[][] availableRooms) {
		this.availableRooms = availableRooms;
	}

}
